[LINUX] Backport PCI MMCONFIG patch from 2.6.17 -- only use MMCONFIG
authorkfraser@localhost.localdomain <kfraser@localhost.localdomain>
Wed, 20 Sep 2006 11:02:13 +0000 (12:02 +0100)
committerkfraser@localhost.localdomain <kfraser@localhost.localdomain>
Wed, 20 Sep 2006 11:02:13 +0000 (12:02 +0100)
access method if the memory area is reserved in the E820 map.
Signed-off-by: Keir Fraser <keir@xensource.com>
patches/linux-2.6.16.29/pci-mmconfig-fix-from-2.6.17.patch [new file with mode: 0644]
patches/linux-2.6.16.29/series

diff --git a/patches/linux-2.6.16.29/pci-mmconfig-fix-from-2.6.17.patch b/patches/linux-2.6.16.29/pci-mmconfig-fix-from-2.6.17.patch
new file mode 100644 (file)
index 0000000..492d135
--- /dev/null
@@ -0,0 +1,284 @@
+diff -pruN ../orig-linux-2.6.16.29/arch/i386/pci/mmconfig.c ./arch/i386/pci/mmconfig.c
+--- ../orig-linux-2.6.16.29/arch/i386/pci/mmconfig.c   2006-09-12 19:02:10.000000000 +0100
++++ ./arch/i386/pci/mmconfig.c 2006-09-20 11:55:32.000000000 +0100
+@@ -12,14 +12,22 @@
+ #include <linux/pci.h>
+ #include <linux/init.h>
+ #include <linux/acpi.h>
++#include <asm/e820.h>
+ #include "pci.h"
++/* aperture is up to 256MB but BIOS may reserve less */
++#define MMCONFIG_APER_MIN     (2 * 1024*1024)
++#define MMCONFIG_APER_MAX     (256 * 1024*1024)
++
++/* Assume systems with more busses have correct MCFG */
++#define MAX_CHECK_BUS 16
++
+ #define mmcfg_virt_addr ((void __iomem *) fix_to_virt(FIX_PCIE_MCFG))
+ /* The base address of the last MMCONFIG device accessed */
+ static u32 mmcfg_last_accessed_device;
+-static DECLARE_BITMAP(fallback_slots, 32);
++static DECLARE_BITMAP(fallback_slots, MAX_CHECK_BUS*32);
+ /*
+  * Functions for accessing PCI configuration space with MMCONFIG accesses
+@@ -29,8 +37,8 @@ static u32 get_base_addr(unsigned int se
+       int cfg_num = -1;
+       struct acpi_table_mcfg_config *cfg;
+-      if (seg == 0 && bus == 0 &&
+-          test_bit(PCI_SLOT(devfn), fallback_slots))
++      if (seg == 0 && bus < MAX_CHECK_BUS &&
++          test_bit(PCI_SLOT(devfn) + 32*bus, fallback_slots))
+               return 0;
+       while (1) {
+@@ -74,8 +82,10 @@ static int pci_mmcfg_read(unsigned int s
+       unsigned long flags;
+       u32 base;
+-      if (!value || (bus > 255) || (devfn > 255) || (reg > 4095))
++      if ((bus > 255) || (devfn > 255) || (reg > 4095)) {
++              *value = -1;
+               return -EINVAL;
++      }
+       base = get_base_addr(seg, bus, devfn);
+       if (!base)
+@@ -146,30 +156,62 @@ static struct pci_raw_ops pci_mmcfg = {
+    Normally this can be expressed in the MCFG by not listing them
+    and assigning suitable _SEGs, but this isn't implemented in some BIOS.
+    Instead try to discover all devices on bus 0 that are unreachable using MM
+-   and fallback for them.
+-   We only do this for bus 0/seg 0 */
++   and fallback for them. */
+ static __init void unreachable_devices(void)
+ {
+-      int i;
++      int i, k;
+       unsigned long flags;
+-      for (i = 0; i < 32; i++) {
+-              u32 val1;
+-              u32 addr;
++      for (k = 0; k < MAX_CHECK_BUS; k++) {
++              for (i = 0; i < 32; i++) {
++                      u32 val1;
++                      u32 addr;
++
++                      pci_conf1_read(0, k, PCI_DEVFN(i, 0), 0, 4, &val1);
++                      if (val1 == 0xffffffff)
++                              continue;
++
++                      /* Locking probably not needed, but safer */
++                      spin_lock_irqsave(&pci_config_lock, flags);
++                      addr = get_base_addr(0, k, PCI_DEVFN(i, 0));
++                      if (addr != 0)
++                              pci_exp_set_dev_base(addr, k, PCI_DEVFN(i, 0));
++                      if (addr == 0 ||
++                          readl((u32 __iomem *)mmcfg_virt_addr) != val1) {
++                              set_bit(i, fallback_slots);
++                              printk(KERN_NOTICE
++                      "PCI: No mmconfig possible on %x:%x\n", k, i);
++                      }
++                      spin_unlock_irqrestore(&pci_config_lock, flags);
++              }
++      }
++}
+-              pci_conf1_read(0, 0, PCI_DEVFN(i, 0), 0, 4, &val1);
+-              if (val1 == 0xffffffff)
++/* NB. Ripped from arch/i386/kernel/setup.c for this Xen bugfix patch. */
++static int __init
++e820_all_mapped(unsigned long s, unsigned long e, unsigned type)
++{
++      u64 start = s;
++      u64 end = e;
++      int i;
++      for (i = 0; i < e820.nr_map; i++) {
++              struct e820entry *ei = &e820.map[i];
++              if (type && ei->type != type)
+                       continue;
+-
+-              /* Locking probably not needed, but safer */
+-              spin_lock_irqsave(&pci_config_lock, flags);
+-              addr = get_base_addr(0, 0, PCI_DEVFN(i, 0));
+-              if (addr != 0)
+-                      pci_exp_set_dev_base(addr, 0, PCI_DEVFN(i, 0));
+-              if (addr == 0 || readl((u32 __iomem *)mmcfg_virt_addr) != val1)
+-                      set_bit(i, fallback_slots);
+-              spin_unlock_irqrestore(&pci_config_lock, flags);
++              /* is the region (part) in overlap with the current region ?*/
++              if (ei->addr >= end || ei->addr + ei->size <= start)
++                      continue;
++              /* if the region is at the beginning of <start,end> we move
++               * start to the end of the region since it's ok until there
++               */
++              if (ei->addr <= start)
++                      start = ei->addr + ei->size;
++              /* if start is now at or beyond end, we're done, full
++               * coverage */
++              if (start >= end)
++                      return 1; /* we're done */
+       }
++      return 0;
+ }
+ static int __init pci_mmcfg_init(void)
+@@ -183,6 +225,15 @@ static int __init pci_mmcfg_init(void)
+           (pci_mmcfg_config[0].base_address == 0))
+               goto out;
++      if (!e820_all_mapped(pci_mmcfg_config[0].base_address,
++                      pci_mmcfg_config[0].base_address + MMCONFIG_APER_MIN,
++                      E820_RESERVED)) {
++              printk(KERN_ERR "PCI: BIOS Bug: MCFG area at %x is not E820-reserved\n",
++                              pci_mmcfg_config[0].base_address);
++              printk(KERN_ERR "PCI: Not using MMCONFIG.\n");
++              goto out;
++      }
++
+       printk(KERN_INFO "PCI: Using MMCONFIG\n");
+       raw_pci_ops = &pci_mmcfg;
+       pci_probe = (pci_probe & ~PCI_PROBE_MASK) | PCI_PROBE_MMCONF;
+diff -pruN ../orig-linux-2.6.16.29/arch/x86_64/pci/mmconfig.c ./arch/x86_64/pci/mmconfig.c
+--- ../orig-linux-2.6.16.29/arch/x86_64/pci/mmconfig.c 2006-09-12 19:02:10.000000000 +0100
++++ ./arch/x86_64/pci/mmconfig.c       2006-09-20 11:55:41.000000000 +0100
+@@ -9,11 +9,19 @@
+ #include <linux/init.h>
+ #include <linux/acpi.h>
+ #include <linux/bitmap.h>
++#include <asm/e820.h>
++
+ #include "pci.h"
+-#define MMCONFIG_APER_SIZE (256*1024*1024)
++/* aperture is up to 256MB but BIOS may reserve less */
++#define MMCONFIG_APER_MIN     (2 * 1024*1024)
++#define MMCONFIG_APER_MAX     (256 * 1024*1024)
++
++/* Verify the first 16 busses. We assume that systems with more busses
++   get MCFG right. */
++#define MAX_CHECK_BUS 16
+-static DECLARE_BITMAP(fallback_slots, 32);
++static DECLARE_BITMAP(fallback_slots, 32*MAX_CHECK_BUS);
+ /* Static virtual mapping of the MMCONFIG aperture */
+ struct mmcfg_virt {
+@@ -55,7 +63,8 @@ static char __iomem *get_virt(unsigned i
+ static char __iomem *pci_dev_base(unsigned int seg, unsigned int bus, unsigned int devfn)
+ {
+       char __iomem *addr;
+-      if (seg == 0 && bus == 0 && test_bit(PCI_SLOT(devfn), &fallback_slots))
++      if (seg == 0 && bus < MAX_CHECK_BUS &&
++              test_bit(32*bus + PCI_SLOT(devfn), fallback_slots))
+               return NULL;
+       addr = get_virt(seg, bus);
+       if (!addr)
+@@ -69,8 +78,10 @@ static int pci_mmcfg_read(unsigned int s
+       char __iomem *addr;
+       /* Why do we have this when nobody checks it. How about a BUG()!? -AK */
+-      if (unlikely(!value || (bus > 255) || (devfn > 255) || (reg > 4095)))
++      if (unlikely((bus > 255) || (devfn > 255) || (reg > 4095))) {
++              *value = -1;
+               return -EINVAL;
++      }
+       addr = pci_dev_base(seg, bus, devfn);
+       if (!addr)
+@@ -129,23 +140,52 @@ static struct pci_raw_ops pci_mmcfg = {
+    Normally this can be expressed in the MCFG by not listing them
+    and assigning suitable _SEGs, but this isn't implemented in some BIOS.
+    Instead try to discover all devices on bus 0 that are unreachable using MM
+-   and fallback for them.
+-   We only do this for bus 0/seg 0 */
++   and fallback for them. */
+ static __init void unreachable_devices(void)
+ {
+-      int i;
+-      for (i = 0; i < 32; i++) {
+-              u32 val1;
+-              char __iomem *addr;
++      int i, k;
++      /* Use the max bus number from ACPI here? */
++      for (k = 0; k < MAX_CHECK_BUS; k++) {
++              for (i = 0; i < 32; i++) {
++                      u32 val1;
++                      char __iomem *addr;
++
++                      pci_conf1_read(0, k, PCI_DEVFN(i,0), 0, 4, &val1);
++                      if (val1 == 0xffffffff)
++                              continue;
++                      addr = pci_dev_base(0, k, PCI_DEVFN(i, 0));
++                      if (addr == NULL|| readl(addr) != val1) {
++                              set_bit(i + 32*k, fallback_slots);
++                              printk(KERN_NOTICE
++                              "PCI: No mmconfig possible on device %x:%x\n",
++                                      k, i);
++                      }
++              }
++      }
++}
+-              pci_conf1_read(0, 0, PCI_DEVFN(i,0), 0, 4, &val1);
+-              if (val1 == 0xffffffff)
++/* NB. Ripped from arch/x86_64/kernel/e820.c for this Xen bugfix patch. */
++static int __init e820_all_mapped(unsigned long start, unsigned long end, unsigned type)
++{
++      int i;
++      for (i = 0; i < e820.nr_map; i++) {
++              struct e820entry *ei = &e820.map[i];
++              if (type && ei->type != type)
+                       continue;
+-              addr = pci_dev_base(0, 0, PCI_DEVFN(i, 0));
+-              if (addr == NULL|| readl(addr) != val1) {
+-                      set_bit(i, &fallback_slots);
+-              }
++              /* is the region (part) in overlap with the current region ?*/
++              if (ei->addr >= end || ei->addr + ei->size <= start)
++                      continue;
++
++              /* if the region is at the beginning of <start,end> we move
++               * start to the end of the region since it's ok until there
++               */
++              if (ei->addr <= start)
++                      start = ei->addr + ei->size;
++              /* if start is now at or beyond end, we're done, full coverage */
++              if (start >= end)
++                      return 1; /* we're done */
+       }
++      return 0;
+ }
+ static int __init pci_mmcfg_init(void)
+@@ -161,6 +201,15 @@ static int __init pci_mmcfg_init(void)
+           (pci_mmcfg_config[0].base_address == 0))
+               return 0;
++      if (!e820_all_mapped(pci_mmcfg_config[0].base_address,
++                      pci_mmcfg_config[0].base_address + MMCONFIG_APER_MIN,
++                      E820_RESERVED)) {
++              printk(KERN_ERR "PCI: BIOS Bug: MCFG area at %x is not E820-reserved\n",
++                              pci_mmcfg_config[0].base_address);
++              printk(KERN_ERR "PCI: Not using MMCONFIG.\n");
++              return 0;
++      }
++
+       /* RED-PEN i386 doesn't do _nocache right now */
+       pci_mmcfg_virt = kmalloc(sizeof(*pci_mmcfg_virt) * pci_mmcfg_config_num, GFP_KERNEL);
+       if (pci_mmcfg_virt == NULL) {
+@@ -169,7 +218,8 @@ static int __init pci_mmcfg_init(void)
+       }
+       for (i = 0; i < pci_mmcfg_config_num; ++i) {
+               pci_mmcfg_virt[i].cfg = &pci_mmcfg_config[i];
+-              pci_mmcfg_virt[i].virt = ioremap_nocache(pci_mmcfg_config[i].base_address, MMCONFIG_APER_SIZE);
++              pci_mmcfg_virt[i].virt = ioremap_nocache(pci_mmcfg_config[i].base_address,
++                                                       MMCONFIG_APER_MAX);
+               if (!pci_mmcfg_virt[i].virt) {
+                       printk("PCI: Cannot map mmconfig aperture for segment %d\n",
+                              pci_mmcfg_config[i].pci_segment_group_number);
index 63774a0bd7a293800a7318e31cca416eca22a560..02d37510836c1f7cac23a4640803ce2701f14032 100644 (file)
@@ -10,6 +10,7 @@ net-gso-1-check-dodgy.patch
 net-gso-2-checksum-fix.patch
 net-gso-3-fix-errorcheck.patch
 net-gso-4-kill-warnon.patch
+pci-mmconfig-fix-from-2.6.17.patch
 pmd-shared.patch
 rcu_needs_cpu.patch
 rename-TSS_sysenter_esp0-SYSENTER_stack_esp0.patch